//
//  ReadMe.swift
//  项目部署
//
//  Created by apple on 16/8/7.
//  Copyright © 2016年 itcast. All rights reserved.
//

import Foundation


/*

    - 为什么使用readMe文件
        - 时间久远 你可能忘记了 如果一年半载你在需要在该项目添加功能或者修改功能  通过readMe文件可以教你快速回忆起项目
        - 如果你做的外包项目 
            - 哪些类是什么意思  一些主要功能点提示
        - 如果你在你上班的时候偷偷的面试去了 然后 你现在是15k 找到一个25k 着急飞
            - 项目交接 

        - 我 我会把一些主要的业务逻辑写在里面 知识点回顾 定义写在里面 便于你们课下复习
=================================================================================
    -第十四天 微博数据缓存




            - status 微博数据
            - statusid 微博id   (复合主键)
            - userid 为了识别哪个用户的数据 (复合主键)
            - createtime  保存数据的时间

            - 保存首页数据分析
            - 当前首页数据是列表数据
            - 建议使用FMDB保存
            - 保存那些数据
                - statusid (下拉刷新 或者上拉加载更多 需要通过sinceid 和maxid) (复合主键)
                - userid (同一个手机 两个登录 使用微博缓存数据 需要区分出 谁的微博数据) (复合主键)
                - status (微博数据 字典-Swift) 需要转成 NSData
                - createtime  (当用户缓存数据的时候 得到一个时间)
                    - 该字段交给数据库来管理 
                        - 代码为 - (datetime('now','localtime'))

            - 新浪微博缓存数据库的表的字段
                - statusid: 该微博的id, 是一个主键
                - status: 该条微博的 JSON 数据
                - userid: 这条微博属于哪个用户 也成为主键，区分两个用户加载回来同一条数据的情况
                - 如果有多个帐号，都要缓存数据，那么如果不区别某些数据是属于哪个帐号的话，那么在没网的情况下去数据库加载数据的话，那么数据就会错乱
                - 还有一个点需要注意：A 用户有一条微博的id是 1234 ，用户B 也关注了 id 为1234这条微博的作者，那么就会出现：主键相同，userid不同，解决方法就是将userid也作为主键，也就是将 statusid和userid作为复合主键

            - 新浪微博的数据库缓存可能会存在什么问题
                - 数据不完整：如果用户在已经有一些缓存数据的情况下，关注了很多用户，但是客户端现在每次加载数据都是从本地去加载，那么，可能关注的用户的微博数据就加载不出来
                - 数据的安全问题：比如有一个用户发了一条违规的微博，这个时候，我们在新浪没有删除这条微博之前就把这条微博给缓存到本地了，那么如果从本地去读取数据的话，就会每次把这条违规的微博加载出来
                - 比如某条微博的转发量是1000，缓存到本地，数据可能不是最真实的



            - 不适合缓存
                - 一些实效性比较高的数据
                - 一些敏感度较高的数据

            - 可以缓存什么样的数据
                - 固定变化不大的数据，并且数据的敏感度不高的数据

            - 如果使用数据库缓存，一定要记得清理缓存


            - 清除缓存
                - 清除缓存的操作一定不要交给用户去做
                - SQLite的数据缓存一定要定时去清理，因为SQLite数据库，清除数据之后，数据库文件大小不会变，因为 sqlite 预估你文件的大小，那么sqlite会保留空间（这样设计是为了尽量少的文件进行扩容操作）
                - SD会自动清除7天以上的图片数据
====================================================================

    - 第十三天回顾
        - 自定义表情键盘frame 微调
        - 删除按钮的定义和设置frame
        - 自定义表情 设置背景颜色
        - 添加pageControl  使用kvc 
                - pageImage
                - currentPageImage
                - 设置pageControl
        - 点击删除按钮 发送通知告知 ComposeVc
        - 点击表情按钮 发送通知告知 ComposeVc
                - 如果是emoji 表情  使用 insertText
                - 如果是图片表情 使用 富文本
                    - 01 先把ComposeTextView 已有的富文本定义一个可变的富文本接收
                    - 02 创建一个文字附件(自定义文字附件)
                            - 给自定义的文字附件的model 赋值
                            - 设置image  (emoticonModel)
                            - 设置bounds (x,y,w,h) (w&h = lineheight)
                    - 03 创建一个不可变的富文本 (传入一个文字附件)
                    - 04 获取当前用户 选择光标的范围
                    - 05 使用可变的富文本替换指定范围的富文本
                    - 06 设置富文本的font
                    - 07 赋值给ComposeTextView
                    - 08 重新设置光标范围
        - 发布图片表情微博
                - 遍历ComposeTextView 富文本
                        - 如果通过key 取到value = nstextAttachment 
                        -  得到model.chs
                - 定义一个字符串 += 当前的富文本中的字符串


        - 首页图文混排
                - 创建一个方法来处理图文混排的问题
                - 使用正则第三方框架
                    - 把服务器返回来的String 转成 NSString
                    - 设置正则规则
                    - 反向遍历
                            - 保存到模型中 range  string
                            - 保存到数组中 
                    - 反向遍历数组 
                            - 到EmoticonTools 中 提供一个方法 遍历里面的默认表情或者浪小花表情 发现如果chs 相同吧对应的模型返回
                    - 01把服务器返回的字符串 转成可变的富文本
                    - 02创建一个文字附件
                            - 设置image
                            - 设置bounds
                    - 03通过一个文字附件 生成一个不可变的富文本
                    - 04通过模型中的range 和已有可变的富文本替换指定range的富文本 然后返回
                    - 05给lable 赋值
                - 抽取到ViewModel中
                        - orginalAttr
                        - retweetAttr
                - 在原创和转发微博中直接使用以上两个属性


====================================================================

    - 第十二天
        - 读取bundle文件 中 default 和 emoji  和 lxh 文件夹下面的info.plist文件 (一维数组)
                - defaut  108  显示6页   20,20,20,20,20,8
                - emoji   80   显示4页   20,20,20,20
                - lxh     40   显示2页   20,20


        - 如何生成一个二维数组
                - default  108  显示6页   20,20,20,20,20,8
                    [[0,19],[20,39],[40,59]...[100,107]]
                - emoji  80  显示4页   20,20,20,20
                    [[0,19],[20,39],[40,59]...[60,79]]
                - lxh  40  显示2页   20,20
                    [[0,19],[20,39]]






====================================================================
    - 第十天
            - 右侧按钮是否可以点击
                - 设置代理 (textView)
            - 自定义textView 里面添加占位label (左侧=5  顶部 = 8)
            - override var font:Font 
                - 通过didSet 监听  改变占位文字的字体
            - 占位文字抛出 可以在外界设置
            - 代理已经在控制器中使用了 所以控件中不能使用了 所以使用监听系统通知
            - 自定义BottomView 
            - 自定义pictureView (0||9 只显示图片) 其他最后一个显示为加号图片
            - 学习uiiimagepikercontroller
            - 得到图片给pictureView 在pictureView提供一个方法 供外界调用 
                - 添加到数组中 最重要 一定要记得刷新UI
            - 监听系统键盘通知 frame 将要改变  endframe  得到他的y轴 更改约束

====================================================================

    - 第九天
        - 自定义下拉刷新控件分析
            - 刷新控件 自定义 需要继承与谁 -> UIControl
            - 添加到谁身上 tableView 
            - frame = (0,-控件高度,屏幕的宽度,控件高度)
            - 状态问题 (正常 下拉中 刷新中)
                - 如果用户在拖动tableView中 关心两种状态 正常和下拉中
                    Y > - 114 && 状态=下拉中 显示正常
                    Y <= - 114 && 状态=正常 显示下拉中
            
                - 反之 松手
                    如果他当前的状态为下拉中 会进入 刷新中
            
























        - 下拉刷新和上拉加载更多(10,9,8,7,6)
            since_id若指定此参数，则返回ID比since_id大的微博（即比since_id时间晚的微博），默认为0。
            max_id若指定此参数，则返回ID小于或等于max_id的微博，默认为0。

        - 如果下拉刷新
                since_id = 10
                max_id = 0
        - 如果是上拉加载更多
                since_id = 0
                max_id = 6 - 1

        - 时间格式化
            - 如果微博时间和当前时间比较
                -如果是今年
                    - 如果是今天
                        - 如果小于60秒  显示 刚刚
                        - 如果大于等于60 && 小于3600  显示 xx分钟前
                        - 其他   显示  xx小时前
                    - 如果是昨天
                        -  2016年08月14日 20:20:20  显示   昨天 20:20
                    - 其他
                        -  2016年06月14日 20:20:20  显示    06月14日 20:20
                - 如果不是今年 
                        -  2014年06月14日 20:20:20  显示 2014年06月14日 20:20

====================================================================
    - 第八天

        - 第八天内容回顾
            - HMStatusModel 中添加一个属性 retweet_status (需要我们自己做字典转模型的操作)
            - 卸载bottomView.top约束
                - 如果有转发微博retweet_status != nil
                    - 赋值
                    - bottomView.top == retweetView.bottom
                    - 显示转发微博
                - 反之 retweet_status == nil
                    - bottomView.top == originalview.bottom
                    - 隐藏转发微博

            - 自定义配图
                - 原创微博
                - 添加字段 pic_urls 其实一个数组 (创建模型 pictureInfo)
                    - 写在原创微博.bottom 约束
                        - 如果pic_urls.count > 0  代表有配图
                            - 赋值
                            - 原创微博.bottom == 配图的.bottom
                            - 显示配图
                        - 反之 代表没有配图
                            - 原创微博.bottom == 微博正文的.bottom
                            - 隐藏配图

                - 转发微博
                    - 添加字段 pic_urls 其实一个数组 (创建模型 pictureInfo)
                        - 写在转发微博.bottom 约束
                            - 如果pic_urls.count > 0  代表有配图
                            - 赋值
                            - 转发微博.bottom == 配图的.bottom
                            - 显示配图
                        - 反之 代表没有配图
                            - 转发微博.bottom == 微博正文的.bottom
                            - 隐藏配图
            

            - 是否是gif图片 通过判断url的后缀是否是 .gif (隐藏和显示)
            - 处理微博来源
                    - 首先判断 是否包含一个特殊字符
                    - 获取 开始和介绍位置字符的range
                    - 通过range 或者index
                    - 通过String subStringrange 方法 完成字符串的截取

            - 处理底部视图 转发评论赞 业务逻辑

                    - 如果 count <= 0  直接显示 转发 评论 赞
                    - 如果 count > 0 && count < 10000  是多少显示多少  示例  8888  8888
                    - 如果 count >= 10000   示例 12000   显示  1.2万
                    - 如果 count%10000 == 0   示例 20000  显示  2万
-------------------------------------------------------------------------------------------

    - 第八天业务逻辑分析

        - 底部视图的转发 评论 赞  业务逻辑
            - 如果 count <= 0  直接显示 转发 评论 赞
            - 如果 count > 0 && count < 10000  是多少显示多少  示例  8888  8888
            - 如果 count >= 10000   示例 12000   显示  1.2万
                    - 如果 count%10000 == 0   示例 20000  显示  2万
        - 转发微博逻辑分析
            - 卸载底部视图的约束
            - 如果retweeted_status == nil  没有转发微博
                - 底部视图的top == 原创微博的bottom
                - 隐藏转发微博(cell复用问题)

            - 反之 有转发微博
                - 赋值
                - 底部视图的top == 转发微博的bottom
                - 显示转发微博(cell复用问题)


            - 由于我们使用的是tableView  那么cell 是复用的 如果滚动tableView 离开屏幕后 cell 到缓存池中 将要显示的cell 从缓存池中那 如果缓存池中没有 直接创建(创建的话 cell 原创转发底部)
                - 但是可能你根本就灭有转发微博 所以需要隐藏
                - 如果下一条显示有转发微博 但是从缓存池中拿出来的cell 的转发微博是隐藏的 那么我们根本看不到 所以要显示转发微博


            - 关于约束问题
                - 如果被约束的对象 相对于的对象没有发生改变 可以使用update
                - 如果被约束的对象 相对于的对象发生了改变 使用update 无效 需要先卸载 然后在约束 如果约束的时候 使用update 和make 均可






====================================================================
    - 第七天

        - 首页开发
            - 请求数据 -> Vc -> 抽取NetworkTools -> 抽取ViewModel中
            - ViewModel 中请求数据和提供数据
            - 分析首页cell 
                - 原创
                - 转发
                - 底部视图
            - StatusViewModel 叫他管理model  给cell提供数据
                - 也就是说需要StatusViewModel 有一个属性 为model 
                - 在StatusViewModel实例化 走构造函数的时候 必须给model 赋值 (通过重载构造函数)
                - 在对StatusViewModel实例化的时候 还要给会员等级和会员认证 计算显示本地的image
                - 当StatusViewModel 实例化ok以后 其身上的属性 均有值  而且不会再改变了
                - 目前其身上的属性 为存储型属性
====================================================================
    - 第六天
        - 上午知识点回顾
            - 通过token 和 uid 获取个人信息数据
                - 把头像和名字的字段保存起来 使用一个模型(userAccountModel)
            - 以上使用的是 AFN 直接请求网络数据
            - 拖入封装好的AFN网络工具类 使用公共方法请求数据
            - 把HMNetworkTools 帮我们创建两个方法 请求 userAccount 和请求userInfo
            - 外界直接使用HMNetworkTools 里面的两个方法请求数据
                - 成功的回调
                - 失败的回调
            - 因为我们使用的MVVM模式开发项目 所以我们要把网络请求的事情交给ViewModel
            - 创建了ViewModel 把请求的事情copy到 ViewModel 而控制器只需要知道成功或者失败(通过闭包告知控制器)
            - 通过归档和解档保存对象
                - 在模型中 NSCoding 
                    - encode
                    - initCode
                - 控制器委托ViewModel 保存对象 和获取对象的方法
                    - saveUserAccount
                    - getUserAccount
            - 为了不重复操作沙盒 所以在当前ViewModel 添加一个属性为userAccount
            - 当外界使用当前ViewModel 这个单例的时候 也就会走哦init方法 我要对userAccount 赋值 也就是从沙盒中获取对象
                - 为了解决第一次使用单例的时候 userAccount 为nil 
                    - 必须在\保存对象的时候 给单例中的属性赋值
            - 在ViewModel 创建一个属性 accessToken 
                - 如果userAccount.accessToken == nil 返回accessToken== nil
                - 反之 如果过期了返回accessToken== nil
                    - 如果没有过期  返回accessToken== userAccount.accessToken
            - 在ViewModel中 创建一个属性 为isLogin
                    - 如果accessToken== nil 代表没有登录
                    - 反之 代表登录了

            - 创建欢迎界面 
                    - 背景图片
                    - 头像
                    - 欢迎回来
                        - 当界面已经显示后 给头像设置阻尼动画
                            - 需要更改约束 update  self.view.layoutIfNeet 强制刷新UI


            - 如果判断根控制器的选择
                - 如果用户登录了  - rootVc = WelComeVc
                - 如果用户没有登录 - rootVc = MainVc(显示的访客视图)
            - 通知的使用
                - 如果用户没有登录
                    -MainVc(显示的访客视图) -> OAuth界面 -> 成功 -> 关闭 OAuth界面 -> 发送通知告知appdelete切换根控制器为 -> WelComeVc -> 动画完成后 -> MainVc(但是显示的不是访客视图 而是登录状态)
                
                - 如果用户登录了
                    - WelComeVc -> 动画完成后 -> MainVc(但是显示的不是访客视图 而是登录状态)








        - 发送通知切换根控制器业务逻辑
            - 如果在第一次登陆 到OAuthVc中 如果保存数据成功 告知appdelegate 切换根控制器为WelComeVc
            - 如果在WelComeVc 动画完成后 告知appdelegate 切换根控制器为 HMMainVc


        - 如果用户没有登陆    window.RootVc == HMMainVc
        - 如果用户已经登陆了   window.RootVc == WelComeVc

====================================================================
    - 第五天
        - 内容预告
            - VisitorView 中的按钮点击事件抛到控制器中
            - 学习单例
            - AFN网络封装
            - OAuth授权介绍
            - 微博开发者平台注册应用
            - 搭建OAuth界面
            - JS注入
            - 监听webView代理方法 截取授权码
            - 通过授权码获取accessToken
            - 通过accessToken 获取个人信息数据
            - ViewModel 抽取
            - 保存用户信息数据 使用归档和解档

        - 知识点讲解
            - OAuth授权
                - open auth  开发的授权

            - 登录&注册界面  
                - 用户名和密码  正确的话   登录成功
                    - 服务器会返回来  或者userId
                    - 当你在请求数据的时候 可能需要userId (人的身份证)
                    - 关注医生  取消关注医生
                        - 我的id = 100
                        - 医生的id == 200
                    - 如果我想获取我关注医生的列表

            - OAuth授权流程介绍
                - 登录微博开发者平台 www.open.weibo.com
                - 注册账号 成功开发者(个人开发者, 公司开发者)
                - 注册应用 
                    - appKey
                    - appSecret
                - 设置回调页
                - 添加测试账号
                - 加载webView页面



====================================================================
    - 第四天 回顾
        - 项目部署到开源中国的码云
        - 按照码云的提示 生成公钥和私钥
        - 把公钥配置的码云中
        - 创建一个共有项目
        - git clone 当前项目克隆到本地
        - 添加忽略文件 一定要先添加
        - 创建项目 和项目相关设置
        - 确定项目文件夹
            - Model
            - View
                - Main
                - Home
                - Message
                - Discover
                - Profile
            - ViewModel 
            - Libs
            - Common
        - 添加子控制器
            - 抽取出一个添加子控制器的公共方法
            - 自定义tabBar (通过kvc 给系统的只读属性赋值)
            - 添加撰写按钮和遍历tabBar子控件设置frame
        - 抽取基类(HMVisitorVc)
        - 自定义VisitorView
            - 设置圆圈动画
            - 提供一个方法供外界调用 在不同的控制器中显示不同的icon和信息
            - 使用可选值 为nil 默认认为是首页 因为首页还要设置圆圈动画
        - Swift中 代理和协议的使用


====================================================================
    - 第三天
        - 项目部署
            - 开源中国  git
            - 两种协议 
                    - HTTPS
                    - SSH


            - RSA秘钥对 - 它是一个非对称加密(加密效率极低)
                - 私钥 对项目加密
                - 公钥 对项目解密
            - 为什么对项目加密
                - 防止项目泄露


            - 加密过程
                - 创建一个私钥
                - 生成一个公钥 
                - 把公钥给服务器
                - 随机生成一个AES秘钥
                - 把AES 通过私钥加密后 通过网络给服务器
                - 通过AES给项目加密
                - 把加密后的项目给服务器
                - 服务器通过公钥把加密后的AES 解密
                - 服务器通过解密后的AES 把项目解密




ssh-keygen -t rsa -C "smile_faner@163.com"


































































































*/